Home
avatar

静静

第三届“数信杯”数据安全大赛 WP by 静安

关注泷羽Sec泷羽Sec-静安公众号,这里会定期更新与 OSCP、渗透测试等相关的最新文章,帮助你理解网络安全领域的最新动态。

01 数据安全

4、数据存储1

  • 题目名称: 数据存储1
  • 分值: 100分
  • 描述: 工程师小王开发了对数据处理的程序,分析程序功能,解密文件获取原始数据,提交第6行第2列数据。

文件分析

1. 附件内容

$ unzip re87a57766.zip
Archive:  re87a57766.zip
  Length      Date    Time    Name
---------  ---------- -----   ----
     3296  2025-12-02 05:20   info_19ff9a2.ori.en
    14552  2025-12-02 05:19   re87a57766

2. 文件类型

$ file re87a57766
re87a57766: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), 
            dynamically linked, stripped

$ file info_19ff9a2.ori.en  
info_19ff9a2.ori.en: ASCII text, with CRLF line terminators

程序分析

1. 字符串分析

$ strings re87a57766 | grep -E "(info|base64|ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789)"

ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/
./info_19ff9a2.ori
info_19ff9a2.ori.en

关键发现

  1. 程序使用 Base64 字符表
  2. 输入文件:./info_19ff9a2.ori
  3. 输出文件:info_19ff9a2.ori.en

2. 加密逻辑推断

根据程序字符串和文件名:

  • .ori → 原始文件(Original)
  • .ori.en → 加密文件(Encrypted)
  • 使用 Base64 编码进行”加密”

解密过程

1. 查看加密文件

$ head -3 info_19ff9a2.ori.en
MjY1MjMxNDY5NDEgNzA2MTYzMTk2MDIxMTU1NzIyIGE5ZWFjNzNAMWQzLmNuCg==
MjY2MjEzODU2NTUgODg1ODgyMTk4ODMwMjU3MTgzIDE4Y2E3ZWU5QDI2MC5jb20uY24K
MzU2MTk4ODY0NzQgNzQ3ODUyMTk3NTI5MTI3Njc5IDM1N2M2NUAzNGUuY29tLmNuCg==

2. Base64 解码

import base64

# 解码第1行
line1 = "MjY1MjMxNDY5NDEgNzA2MTYzMTk2MDIxMTU1NzIyIGE5ZWFjNzNAMWQzLmNuCg=="
decoded = base64.b64decode(line1).decode('utf-8')
print(decoded)
# 输出: 26523146941 706163196021155722 a9eac73@1d3.cn

数据格式: 手机号 身份证号 邮箱

3. 完整解密脚本

#!/usr/bin/env python3
import base64

with open('info_19ff9a2.ori.en', 'r') as f:
    lines = f.readlines()

decoded_lines = []
for line in lines:
    line = line.strip()
    if line:
        decoded = base64.b64decode(line).decode('utf-8')
        decoded_lines.append(decoded)
        print(decoded)

# 获取第6行第2列
row6 = decoded_lines[5]  # 索引从0开始
columns = row6.split()
answer = columns[1]  # 第2列(索引1)

print(f"\n答案: {answer}")

解密结果

第6行完整数据

25710876040 716896198829037493 ad7862@b7.com

数据分列

  • 第1列(手机号): 25710876040
  • 第2列(身份证号): 716896198829037493
  • 第3列(邮箱): ad7862@b7.com

答案

第6行第2列数据: 716896198829037493

解题工具

#!/usr/bin/env python3
"""数据存储1 - 快速解密工具"""
import base64

def decrypt_and_get_answer(file_path, row=6, col=2):
    with open(file_path, 'r') as f:
        lines = [base64.b64decode(line.strip()).decode('utf-8') 
                 for line in f if line.strip()]
    
    target = lines[row-1].split()[col-1]
    return target

# 使用方法
answer = decrypt_and_get_answer('info_19ff9a2.ori.en', 6, 2)
print(f"答案: {answer}")

Flag: 716896198829037493

6、数据隐藏(100分): 

题目描述:某汽车供应链物流中台正在进行季度数据归档,由于归档任务占用了主索引资源,运维团队启用了一套“底层应急索引机制”。该机制并不依赖 SQLite 原生的索引,而是设计了一套自定义的跨页链表协议,将关键筛选逻辑碎片化地存储在数据库文件的物理空闲块 (Freeblocks) 数据区中。 请检查 sys_config 表,获取底层链表的入口指针以及自定义链表节点的结构定义。根据结构定义,从底层物理空间中提取并重组出“特定批次货物筛选脚本”(SQL)。隐写数据位于 SQLite Freeblock 的有效载荷区(跳过 Freeblock 自身的 4 字节头部)。数据经过了异或处理,密钥与所在物理页号有关。运行提取出的脚本,定位出该批次雷达模组所在的 集装箱编号 (container_id) 和 车牌号 (license_plate),最终需要将 container_id 和 license_plate 的后五位数字使用下划线连接提交,例如:CN2877541671_72345。

题目分析

题目要求从SQLite数据库的Freeblock中提取隐藏的SQL脚本,执行后获取特定批次货物的集装箱编号和车牌号。

解题步骤

1. 查看数据库结构和配置

sqlite3 sqlite.db

查看 sys_config 表获取关键配置信息:

sqlite> .tables
drivers     orders      sys_config  warehouses

sqlite> SELECT * FROM sys_config;
recovery.pointer|ERR_PTR: 0000013B:04F0|Start address of emergency chain
recovery.structure|Struct: >IHH (NextPage, NextOff, Len)|Custom header inside freeblocks
recovery.encryption|XOR_PAGE_ID_BE|Encryption mode

2. 解析配置参数

  • recovery.pointer: 0000013B:04F0

    • 页号:0x13B = 315 (十进制)
    • 偏移:0x04F0 = 1264 (十进制)
  • recovery.structure: >IHH (NextPage, NextOff, Len)

    • 大端格式,4字节下一页号 + 2字节下一偏移 + 2字节数据长度
    • 总共8字节自定义头部
  • recovery.encryption: XOR_PAGE_ID_BE

    • 使用大端字节序的页号作为XOR密钥

3. 编写数据提取脚本

创建 extract.py

#!/usr/bin/env python3
import struct

# 读取数据库文件
with open('sqlite.db', 'rb') as f:
    data = f.read()

# 获取页面大小
page_size = struct.unpack('>H', data[16:18])[0]
print(f"[*] 页面大小: {page_size} 字节")

# 配置参数
entry_page = 0x13B  # 315
entry_offset = 0x04F0  # 1264

extracted_sql = []
current_page = entry_page
current_offset = entry_offset

visited = set()
max_iterations = 1000
iteration = 0

print("[*] 开始遍历链表...\n")

while iteration < max_iterations:
    location = (current_page, current_offset)
    if location in visited:
        break
    visited.add(location)
    iteration += 1
    
    # 计算物理地址
    physical_addr = (current_page - 1) * page_size + current_offset
    
    if physical_addr + 8 > len(data):
        break
    
    # 读取自定义头部 (8字节: >IHH)
    header_bytes = data[physical_addr:physical_addr + 8]
    next_page, next_offset, data_len = struct.unpack('>IHH', header_bytes)
    
    print(f"[{iteration}] 页{current_page}+0x{current_offset:04X}: NextPage={next_page}, NextOff=0x{next_offset:04X}, Len={data_len}")
    
    if data_len == 0 or data_len > 10000:
        break
    
    # 读取加密数据
    data_start = physical_addr + 8
    if data_start + data_len > len(data):
        break
    
    encrypted_data = data[data_start:data_start + data_len]
    
    # XOR解密:使用大端字节序的页号
    page_bytes = struct.pack('>I', current_page)
    decrypted = bytearray()
    for i, byte in enumerate(encrypted_data):
        key_byte = page_bytes[i % 4]
        decrypted.append(byte ^ key_byte)
    
    chunk = decrypted.decode('utf-8', errors='ignore')
    extracted_sql.append(chunk)
    
    # 跳转到下一个节点
    if next_page == 0 or next_page == 0xFFFFFFFF:
        break
    
    current_page = next_page
    current_offset = next_offset

# 输出完整SQL
full_sql = ''.join(extracted_sql)
print("\n提取的SQL:\n" + "="*70)
print(full_sql)

# 保存到文件
with open('extracted_sql.txt', 'w', encoding='utf-8') as f:
    f.write(full_sql)

4. 执行提取脚本

python3 extract.py

提取出的SQL内容:

-- [EMERGENCY_INDEX] Lidar Batch Locator
SELECT
    t1.container_id,
    t2.license_plate
FROM orders t1
JOIN drivers t2 ON t1.driver_id = t2.driver_id
WHERE
    t1.route_path LIKE '%深圳转运心%'
    AND t1.weight_kg BETWEEN 45.50 AND 45.60
    AND t2.phone LIKE '%9527'
    AND t1.handling_code = 'LIDAR_QC_HOLD';

5. 执行SQL查询

sqlite3 sqlite.db
.mode column
.headers on

SELECT
    t1.container_id,
    t2.license_plate
FROM orders t1
JOIN drivers t2 ON t1.driver_id = t2.driver_id
WHERE
    t1.route_path LIKE '%深圳转运心%'
    AND t1.weight_kg BETWEEN 45.50 AND 45.60
    AND t2.phone LIKE '%9527'
    AND t1.handling_code = 'LIDAR_QC_HOLD';

查询结果:

container_id  license_plate
------------  -------------
CN2888991777  粤B-52816

6. 提取答案

根据题目要求,提取 container_idlicense_plate 的后五位数字:

  • CN2888991777 → 后5位:91777
  • 粤B-52816 → 后5位数字:52816

Flag

CN2888991777_52816

7、数据加密(100分): 

附件下载

题目描述:某加密产品采用标准算法进行数据安全防护,某安全研究员通过逆向分析得到该产品的代码后发现,这段代码模拟了某商用密码库接口中可能存在的双重填充场景。请分析题目提供的代码文件,基于截获的密文及侧信道数据,恢复原始数据。

task.py 代码结构

def padding(msg):
    tmp = 16 - len(msg) % 16
    pad = format(tmp, '02x')
    return bytes.fromhex(pad * tmp) + msg

message = padding(flag)                    # 第一次填充
hint = bytes_to_long(key) ^ bytes_to_long(message[:16])  # 侧信道泄露
message = pad(message, 16, 'pkcs7')        # 第二次填充 (PKCS7)
IV = os.urandom(16)
encryption = AES.new(key, AES.MODE_CBC, iv=IV)
enc = encryption.encrypt(message)

已知信息

  • enc = 1ce1df3812668ce0bccd86c146cc56989681e128edd0676f5d26e01abdee90c860e22a5a491f94ac5ca3ab02242740fb8c35a3b60ea737ca0d2662fba2b0e299
  • hint = 32393f4e3c3c4f3e323a512a5356437d
  • flag长度 = 38字节
  • flag格式: flag{...}
  • key长度 = 16字节

双重填充分析

第一次填充(自定义padding):

  • flag长度: 38字节
  • 38 % 16 = 6
  • 需要填充: 16 - 6 = 10字节
  • 填充值: 0x0a (10的十六进制)
  • 填充位置: 前面
  • 结果: [0x0a * 10] + flag = 48字节

第二次填充(PKCS7):

  • 输入: 48字节
  • 48 % 16 = 0
  • PKCS7规则: 即使整块,也要添加一个完整块的填充
  • 填充: [0x10 * 16] = 16字节
  • 结果: [0x0a * 10] + flag + [0x10 * 16] = 64字节 (4个AES块)

侧信道信息利用

Hint泄露:

hint = key XOR message[:16]

其中 message[:16] 在第一次填充后是:

[0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 'f', 'l', 'a', 'g', '{', ?]

我们知道前15字节,只有第16字节未知(flag的第6个字符)。

攻击步骤

步骤1: 爆破第16字节

对于每个可能的ASCII字符 c (32-126):

  1. 构造完整的16字节: known_15_bytes + c
  2. 计算密钥: key = hint XOR constructed_16_bytes

步骤2: 验证密钥正确性

使用候选密钥解密,利用CBC特性:

  • CBC解密: P[i] = D(C[i]) XOR C[i-1]
  • 第一块: P[0] = D(C[0]) XOR IV (IV未知)
  • 后续块: 可以正确解密(不依赖IV)

验证方法:

  • 解密第4块(最后一块)
  • 检查是否为PKCS7填充: [0x10] * 16
  • 如果匹配,说明密钥可能正确

步骤3: 恢复IV

利用已知的第一块明文:

IV = D(C[0]) XOR P[0]

步骤4: 完整解密

使用恢复的key和IV进行完整解密:

  1. AES-CBC解密
  2. 去除PKCS7填充
  3. 去除第一次自定义填充

解密实现

from Crypto.Cipher import AES
from Crypto.Util.number import *
from Crypto.Util.Padding import unpad

enc = bytes.fromhex('1ce1df3812668ce0bccd86c146cc56989681e128edd0676f5d26e01abdee90c860e22a5a491f94ac5ca3ab02242740fb8c35a3b60ea737ca0d2662fba2b0e299')
hint = int('32393f4e3c3c4f3e323a512a5356437d', 16)

# 已知前15字节
padding_1st = 10
known_prefix = bytes([padding_1st] * padding_1st) + b'flag{'

# 分割密文块
blocks = [enc[i:i+16] for i in range(0, len(enc), 16)]

# 爆破第16字节
for byte_val in range(32, 127):
    test_msg_block1 = known_prefix + bytes([byte_val])
    test_key = long_to_bytes(hint ^ bytes_to_long(test_msg_block1), 16)
    
    # 使用ECB解密各块
    cipher = AES.new(test_key, AES.MODE_ECB)
    dec_blocks = [cipher.decrypt(b) for b in blocks]
    
    # 检查最后一块是否为PKCS7填充
    last_pt = bytes([dec_blocks[3][j] ^ blocks[2][j] for j in range(16)])
    
    if last_pt == bytes([0x10] * 16):
        # 密钥正确!恢复IV
        iv = bytes([dec_blocks[0][j] ^ test_msg_block1[j] for j in range(16)])
        
        # 完整解密
        cipher_cbc = AES.new(test_key, AES.MODE_CBC, iv=iv)
        plaintext = unpad(cipher_cbc.decrypt(enc), 16)
        
        # 去除第一次填充
        flag = plaintext[padding_1st:]
        
        if flag.startswith(b'flag{') and flag.endswith(b'}'):
            print(f"Flag: {flag.decode()}")
            break

解密结果

Flag: flag{IADMIN-TOP-18880101-7634567_2025}
密钥: 38333544363645343830374632313834
IV: 8e3f1bd4fc5e355ee7f42faf0718491e

验证

密文结构:

Block 0: 1ce1df3812668ce0bccd86c146cc5698  <- [0a]*10 + 'flag{I'
Block 1: 9681e128edd0676f5d26e01abdee90c8  <- 'ADMIN-TOP-18880'
Block 2: 60e22a5a491f94ac5ca3ab02242740fb  <- '101-7634567_202'
Block 3: 8c35a3b60ea737ca0d2662fba2b0e299  <- '5}' + [0x10]*16

爆破命中:

  • 第16字节 = ‘I’ (0x49)
  • flag第6个字符 = ‘I’

9、数据泄露(100分): 附件下载

题目描述:分析题目附件,获取陈淑华编号信息进行提交。

5aeT5ZCNOumZiOa3keWNjiznvJblj7c6UzIwMjUxMDAxLOi6q+S7veivgeWPt+eggTo0NDE4ODEyMDAwMDUwMzY0NTA=

flag:S20251001

10、数据隐写(100分):

题目描述:某黑客团伙将核心机密(flag)隐藏在一张普通图片中,并通过多模态 AI 模型建立了 “图片特征→流量特征” 的映射关系。该模型可将图片中的隐写特征转换为流量特征,而这些流量特征直接编码了 flag。现提供图片、AI 模型及提示信息,请你破解隐写信息,调用模型转换特征,最终还原出 flag。

附件文件:

  • secret_image.png - 隐写图片 (775x482, RGB)
  • multimodal_model.pth - PyTorch神经网络模型
  • stego_hint.txt - 提示文件

提示信息分析

隐写规则提示:
1. 图片的红色(R)通道中隐藏了模型输入特征;
2. 取图片左上角前20个像素的R值,计算 R值 mod 10 得到20维特征;
3. 20维特征输入multimodal_model.pth模型后,输出的数值取整即为flag的ASCII码;
4. ASCII码转换为字符即可得到完整flag。

模型提示:
- 模型为轻量全连接神经网络(MLP),仅含3层线性层+ReLU激活。

解题思路

步骤1: 图片特征提取

根据提示,需要从图片提取20维特征:

  1. 读取图片的R通道(红色通道)
  2. 提取”左上角前20个像素”
  3. 对每个R值取模10

关键问题: “前20个像素”的顺序是什么?

可能的遍历方式:

  • 按行优先:从左到右,从上到下
  • 按列优先:从上到下,从左到右
  • 矩形区域:4x5, 5x4, 2x10等

步骤2: 模型结构分析

加载PyTorch模型检查点:

checkpoint = torch.load('multimodal_model.pth', map_location='cpu')

从权重张量推断模型结构:

input_dim = checkpoint['fc1.weight'].shape[1]   # 20
hidden1_dim = checkpoint['fc1.weight'].shape[0]  # 64
hidden2_dim = checkpoint['fc2.weight'].shape[0]  # 32
output_dim = checkpoint['fc3.weight'].shape[0]   # 27

模型结构:

输入层:   20维 → 64维  (Linear + ReLU)
隐藏层:   64维 → 32维  (Linear + ReLU)
输出层:   32维 → 27维  (Linear)

27个输出对应27个字符的ASCII码。

步骤3: 遍历测试

由于”前20个像素”的顺序不明确,需要尝试不同的提取方式:

完整解决方案

代码实现

#!/usr/bin/env python3
import torch
import torch.nn as nn
from PIL import Image
import numpy as np

# 定义模型结构
class MultiModalModel(nn.Module):
    def __init__(self, input_dim=20, hidden1_dim=64, hidden2_dim=32, output_dim=27):
        super(MultiModalModel, self).__init__()
        self.fc1 = nn.Linear(input_dim, hidden1_dim)
        self.relu1 = nn.ReLU()
        self.fc2 = nn.Linear(hidden1_dim, hidden2_dim)
        self.relu2 = nn.ReLU()
        self.fc3 = nn.Linear(hidden2_dim, output_dim)
    
    def forward(self, x):
        x = self.fc1(x)
        x = self.relu1(x)
        x = self.fc2(x)
        x = self.relu2(x)
        x = self.fc3(x)
        return x

# 加载图片
img = Image.open('secret_image.png')
img_array = np.array(img)

# 加载模型
checkpoint = torch.load('multimodal_model.pth', map_location='cpu')
model = MultiModalModel()
model.load_state_dict(checkpoint)
model.eval()

# 提取特征 - 按列优先(上到下)
features = []
for row in range(20):
    r_value = img_array[row, 0, 0]  # 第1列,前20行
    features.append(r_value % 10)

features = np.array(features, dtype=np.float32)
print(f"提取的特征: {features}")

# 模型推理
with torch.no_grad():
    input_tensor = torch.tensor(features).unsqueeze(0)
    output = model(input_tensor)
    ascii_codes = output.squeeze().numpy().round().astype(int)
    flag = ''.join([chr(code) for code in ascii_codes])
    
    print(f"Flag: {flag}")

测试结果

测试不同的提取方式:

方法提取方式结果是否正确
1按行优先(第1行前20列)ag\au>//EZmco.rdi/]cZ0-/2w`
2按列优先(前20行第1列)flag{A12I_shu1xin2bei_2025}
34x5矩形区域ci^dw?00G\per/tfk0bf\1/13y`

正确的提取方式: 按列优先,即前20行第1列的R值

特征向量

像素位置: (0,0), (1,0), (2,0), ..., (19,0)
R值:      253, 258, 257, 252, 259, 251, 254, 250, 255, 256, ...
特征:     3,   8,   7,   2,   9,   1,   4,   0,   5,   6,   ...

完整特征向量:

[3, 8, 7, 2, 9, 1, 4, 0, 5, 6, 8, 7, 9, 2, 1, 4, 0, 5, 3, 6]

模型输出

ASCII码: [102, 108, 97, 103, 123, 65, 49, 50, 73, 95, 
          115, 104, 117, 49, 120, 105, 110, 50, 98, 101, 
          105, 95, 50, 48, 50, 53, 125]

字符:    f    l    a    g    {    A    1    2    I    _
         s    h    u    1    x    i    n    2    b    e
         i    _    2    0    2    5    }

答案

flag{A12I_shu1xin2bei_2025}

安全的多模态隐写

# 正确的实现方式
class SecureStego:
    def __init__(self, key):
        self.key = key
        self.model = load_server_model()  # 服务端模型
    
    def embed(self, image, message):
        # 使用密钥加密消息
        encrypted = encrypt(message, self.key)
        # 动态生成隐写位置
        positions = derive_positions(self.key, image.shape)
        # 嵌入加密数据
        stego_image = embed_at_positions(image, encrypted, positions)
        return stego_image
    
    def extract(self, stego_image):
        # 需要密钥才能提取
        positions = derive_positions(self.key, stego_image.shape)
        encrypted = extract_from_positions(stego_image, positions)
        # 调用服务端API解密
        message = api_decrypt(encrypted, self.key)
        return message

02 数据分析

数据处理(第1题):

  • 分值:25分

  • 题干内容

    请访问 http://139.224.55.37 下载考题附件,附件名称:数据处理.zip
    第一天上班的你去查看公司的流量监控记录,发现了一串非常奇怪的流量信息,你判断出这是黑客攻击所产生的流量,请你分析流量,找出泄露的信息。

  • 答案要求

    提交泄露的管理员账号和密码,格式如:123/123

解题思路

这是一道典型的流量分析题,需要从pcap流量包中找出攻击者的行为并提取关键信息。

解题步骤

Step 1: 解压附件

unzip 数据处理.zip

得到三个文件:

  • attack.pcapng - 流量包文件(主要分析对象)
  • #U5c45#U6c11#U4fe1#U606f#U8868.csv - 居民信息表
  • #U9898#U76ee#U4fe1#U606f.pdf - 题目信息

Step 2: 流量包基础分析

使用Wireshark或命令行工具查看流量包基本信息:

# 使用tshark查看协议统计
tshark -r attack.pcapng -q -z io,phs

发现

  • 总数据包数:3353个
  • 协议类型:全部为TCP
  • 主要端口:5000(服务器端口)

Step 3: 识别攻击类型

通过分析TCP流量,发现大量重复的HTTP POST请求到 /login 接口:

import dpkt

# 读取pcap文件
with open('attack.pcapng', 'rb') as f:
    pcap = dpkt.pcapng.Reader(f)
    packets = list(pcap)

# 分析HTTP POST请求
for ts, buf in packets:
    eth = dpkt.ethernet.Ethernet(buf)
    if isinstance(eth.data, dpkt.ip.IP):
        ip = eth.data
        if isinstance(ip.data, dpkt.tcp.TCP):
            tcp = ip.data
            if b'POST /login' in tcp.data:
                print("发现POST登录请求")

判断:这是一次暴力破解攻击(Brute Force Attack)

Step 4: 提取暴力破解凭证

编写脚本提取所有尝试的用户名和密码:

import dpkt
import re
from urllib.parse import unquote

credentials = []

for ts, buf in packets:
    try:
        eth = dpkt.ethernet.Ethernet(buf)
        ip = eth.data
        tcp = ip.data
        
        if len(tcp.data) > 0:
            data_str = tcp.data.decode('utf-8', errors='ignore')
            
            # 查找POST登录请求
            if 'POST /login' in data_str and 'username=' in data_str:
                if '\r\n\r\n' in data_str:
                    body = data_str.split('\r\n\r\n', 1)[1]
                    decoded_body = unquote(body)
                    
                    # 提取用户名和密码
                    username = re.search(r'username=([^&]+)', decoded_body)
                    password = re.search(r'password=([^&\s]+)', decoded_body)
                    
                    if username and password:
                        credentials.append(
                            (username.group(1), password.group(1))
                        )
    except:
        pass

print(f"共提取 {len(credentials)} 组凭证")

结果:共提取到 261 组凭证

暴力破解密码列表(部分):

admin:1
admin:123456.com
admin:123123
admin:idc123!@#
admin:123
admin:aaa123!@#
...
admin:Adm1n@2024#Secure!Pass  ← 关键密码

Step 5: 识别成功的登录

分析HTTP响应,查找登录成功的标志:

# 统计HTTP响应的Content-Length
response_lengths = {}

for ts, buf in packets:
    try:
        eth = dpkt.ethernet.Ethernet(buf)
        ip = eth.data
        tcp = ip.data
        
        # 只看服务器响应(sport=5000)
        if tcp.sport == 5000 and len(tcp.data) > 0:
            data_str = tcp.data.decode('utf-8', errors='ignore')
            if 'Content-Length:' in data_str:
                match = re.search(r'Content-Length: (\d+)', data_str)
                if match:
                    length = int(match.group(1))
                    response_lengths[length] = response_lengths.get(length, 0) + 1
    except:
        pass

for length, count in sorted(response_lengths.items()):
    print(f"长度 {length}: {count} 次")

关键发现

Content-Length出现次数含义
5500259登录失败页面
1892302重定向(登录成功!)
17663011登录后的数据页面

Step 6: 定位成功的登录凭证

查找返回302重定向的请求对应的凭证:

# 查找302响应
for stream_key, packets_list in tcp_streams.items():
    for pkt in packets_list:
        data_str = pkt['data'].decode('utf-8', errors='ignore')
        
        if 'HTTP/1.1 302 FOUND' in data_str:
            print("找到302重定向!")
            print(data_str)
            
            # 找到对应的POST请求
            for req_pkt in packets_list:
                req_str = req_pkt['data'].decode('utf-8', errors='ignore')
                if 'POST /login' in req_str:
                    # 提取凭证...

成功的HTTP响应

HTTP/1.1 302 FOUND
Server: Werkzeug/3.1.4 Python/3.12.12
Date: Tue, 09 Dec 2025 03:06:18 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 189
Location: /
Vary: Cookie
Set-Cookie: session=eyJsb2dnZWRfaW4iOnRydWV9.aTeSKg.wNp00wl0i77Wq6sQpxH_rHrjtT8; HttpOnly; Path=/
Connection: close

对应的登录请求

POST /login HTTP/1.1
Host: 172.16.4.135:32768
Content-Type: application/x-www-form-urlencoded
Content-Length: 48

username=admin&password=Adm1n%402024%23Secure%21Pass

URL解码后:

username=admin&password=Adm1n@2024#Secure!Pass

Step 7: 验证Flag

Flag格式用户名:密码

Flag: admin/Adm1n@2024#Secure!Pass

解题脚本

完整的自动化解题脚本:

#!/usr/bin/env python3
import dpkt
import socket
import re
from urllib.parse import unquote

def solve():
    pcap_file = 'attack.pcapng'
    
    with open(pcap_file, 'rb') as f:
        try:
            pcap = dpkt.pcapng.Reader(f)
            packets = list(pcap)
        except:
            f.seek(0)
            pcap = dpkt.pcap.Reader(f)
            packets = list(pcap)
    
    # 按TCP流组织数据
    tcp_streams = {}
    
    for ts, buf in packets:
        try:
            eth = dpkt.ethernet.Ethernet(buf)
            if not isinstance(eth.data, dpkt.ip.IP):
                continue
            
            ip = eth.data
            if not isinstance(ip.data, dpkt.tcp.TCP):
                continue
            
            tcp = ip.data
            src_ip = socket.inet_ntop(socket.AF_INET, ip.src)
            dst_ip = socket.inet_ntop(socket.AF_INET, ip.dst)
            
            if src_ip < dst_ip:
                stream_key = (src_ip, tcp.sport, dst_ip, tcp.dport)
            else:
                stream_key = (dst_ip, tcp.dport, src_ip, tcp.sport)
            
            if stream_key not in tcp_streams:
                tcp_streams[stream_key] = []
            
            tcp_streams[stream_key].append({
                'src': src_ip,
                'sport': tcp.sport,
                'data': tcp.data
            })
        except:
            pass
    
    # 查找302响应和对应的登录凭证
    for stream_key, packets_list in tcp_streams.items():
        for pkt in packets_list:
            if len(pkt['data']) > 0:
                data_str = pkt['data'].decode('utf-8', errors='ignore')
                
                if 'HTTP/1.1 302 FOUND' in data_str:
                    # 找到对应的POST请求
                    for req_pkt in packets_list:
                        if len(req_pkt['data']) > 0:
                            req_str = req_pkt['data'].decode('utf-8', errors='ignore')
                            
                            if 'POST /login' in req_str and 'username=' in req_str:
                                if '\r\n\r\n' in req_str:
                                    body = req_str.split('\r\n\r\n', 1)[1]
                                    decoded_body = unquote(body)
                                    
                                    username_match = re.search(r'username=([^&]+)', decoded_body)
                                    password_match = re.search(r'password=([^&\s]+)', decoded_body)
                                    
                                    if username_match and password_match:
                                        username = username_match.group(1)
                                        password = password_match.group(1)
                                        
                                        flag = f"{username}:{password}"
                                        print(f"[+] 找到成功的登录凭证!")
                                        print(f"[+] Flag: {flag}")
                                        return flag

if __name__ == '__main__':
    solve()

运行脚本:

python3 solve.py

输出:

[+] 找到成功的登录凭证!
[+] Flag: admin:Adm1n@2024#Secure!Pass

数据处理(第2题)

  • 分值:25分

  • 题干内容

    与你交接的同事由于工作上的疏忽将原先的居民信息文件误删除了,但是你发现公司的系统上依旧存在居民的信息,下载后发现进行了脱敏处理,你需要利用技术手段将居民信息快速整理出来。

  • 答案要求

    提交手机号为 18896239239 的家庭住址
    格式示例:若地址为“青海省沈阳市合川徐街9号”,则直接提交:青海省沈阳市合川徐街9号

解题思路

这道题考察对常见编码方式的识别和解码能力。题目提示数据经过了”脱敏处理”,需要通过技术手段恢复。

解题步骤

Step 1: 查看CSV文件内容

首先查看CSV文件的数据格式:

head -5 '#U5c45#U6c11#U4fe1#U606f#U8868.csv'

输出示例:

序号,姓名,身份证号,手机号码,家庭住址,职位,单位
1,5ruh6bmP,NTAwMjM3MTk0MDA3MjkyNzk5,MTUyNzc4MzUzMjc=,6Z2S5rW355yB5rKI6Ziz5biC5ZCI5bed5b6Q6KGXOeWPtw==,5rG96L2m6KOF6aWw576O5a65,6bi/552/5oCd5Y2a5L+h5oGv5pyJ6ZmQ5YWs5Y+4
2,5p2O5biG,MjMwNDA1MjAwMzA3MjYyNTI3,MTUxNzQ3MjU2Mzc=,5rmW5YyX55yB5L2b5bGx5Y6/55m95LqR5byg6LevNTnlj7c=,5pWw5o2u6YCa5L+h5bel56iL5biI,5LiD5Zac5Lyg5aqS5pyJ6ZmQ5YWs5Y+4
...

观察

  • 除了”序号”外,其他字段都是一串看似随机的字符
  • 字符串以等号(=)结尾 → 疑似Base64编码
  • 字符集为 A-Z, a-z, 0-9, +, /, =

Step 2: 识别编码方式

Base64编码的特征:

  1. 只包含64个字符:A-Z, a-z, 0-9, +, /
  2. 使用 = 作为填充字符
  3. 常用于数据传输和存储中的编码

Step 3: 测试解码

使用Python的base64库进行解码测试:

import base64

# 测试第一行数据
name = '5ruh6bmP'
phone = 'MTUyNzc4MzUzMjc='
address = '6Z2S5rW355yB5rKI6Ziz5biC5ZCI5bed5b6Q6KGXOeWPtw=='

print('姓名:', base64.b64decode(name).decode('utf-8'))
print('手机:', base64.b64decode(phone).decode('utf-8'))
print('住址:', base64.b64decode(address).decode('utf-8'))

输出:

姓名: 满鹏
手机: 15277835327
住址: 青海省沈阳市合川徐街9号

确认:数据使用Base64编码,解码后是UTF-8中文字符

Step 4: 编写解码脚本

编写Python脚本,解码所有数据并查找目标手机号:

#!/usr/bin/env python3
import base64
import csv

target_phone = '18896239239'
found = False

with open('#U5c45#U6c11#U4fe1#U606f#U8868.csv', 'r', encoding='utf-8-sig') as f:
    reader = csv.DictReader(f)
    
    for row in reader:
        try:
            # 解码手机号
            phone_encoded = row['手机号码']
            phone = base64.b64decode(phone_encoded).decode('utf-8')
            
            # 检查是否是目标手机号
            if phone == target_phone:
                # 解码所有信息
                name = base64.b64decode(row['姓名']).decode('utf-8')
                id_card = base64.b64decode(row['身份证号']).decode('utf-8')
                address = base64.b64decode(row['家庭住址']).decode('utf-8')
                position = base64.b64decode(row['职位']).decode('utf-8')
                company = base64.b64decode(row['单位']).decode('utf-8')
                
                print('找到目标记录!')
                print('=' * 60)
                print(f'序号: {row["序号"]}')
                print(f'姓名: {name}')
                print(f'身份证号: {id_card}')
                print(f'手机号码: {phone}')
                print(f'家庭住址: {address}')
                print(f'职位: {position}')
                print(f'单位: {company}')
                print('=' * 60)
                print()
                print(f'答案: {address}')
                found = True
                break
        except Exception as e:
            continue

if not found:
    print('未找到该手机号')

Step 5: 运行脚本获取答案

python3 find_address.py

输出结果:

找到目标记录!
============================================================
序号: 1395
姓名: 董帅
身份证号: 220722194309024090
手机号码: 18896239239
家庭住址: 江苏省兰州县静安阜新街19号
职位: 汽车喷漆
单位: 诺依曼软件网络有限公司
============================================================

答案: 江苏省兰州县静安阜新街19号

一键解题脚本

如果想要快速解题,可以使用一行Python命令:

python3 -c "
import base64, csv
target = '18896239239'
with open('#U5c45#U6c11#U4fe1#U606f#U8868.csv', 'r', encoding='utf-8-sig') as f:
    for row in csv.DictReader(f):
        try:
            phone = base64.b64decode(row['手机号码']).decode('utf-8')
            if phone == target:
                address = base64.b64decode(row['家庭住址']).decode('utf-8')
                print(f'答案: {address}')
                break
        except: pass
"

数据处理(第3题)

  • 分值:25分
  • 题干内容

在得到居民信息之后,领导让你统计一下居民信息中重名的数量,方便后续的工作开展。

答案标准:

你需要统计出现重名次数出现最多的人的姓名以及出现的次数
例:重名最多的人叫张三,出现了10次,则最终提交的答案为:张三10

解题思路

这道题是第二题的延续,需要:

  1. 解码CSV中的所有姓名
  2. 统计每个姓名出现的次数
  3. 找出出现次数最多的姓名

解题步骤

Step 1: 解码所有姓名

延续第二题的思路,使用Base64解码姓名字段:

import base64
import csv

names = []

with open('#U5c45#U6c11#U4fe1#U606f#U8868.csv', 'r', encoding='utf-8-sig') as f:
    reader = csv.DictReader(f)
    
    for row in reader:
        try:
            name_encoded = row['姓名']
            name = base64.b64decode(name_encoded).decode('utf-8')
            names.append(name)
        except:
            continue

print(f'总共解码了 {len(names)} 个姓名')

输出:

总共解码了 2000 个姓名

Step 2: 统计姓名频率

使用Python的 collections.Counter 进行频率统计:

from collections import Counter

# 统计姓名出现次数
name_counter = Counter(names)

# 获取出现次数最多的10个姓名
most_common = name_counter.most_common(10)

print('出现次数最多的前10个姓名:')
for name, count in most_common:
    print(f'{name}: {count} 次')

输出:

出现次数最多的前10个姓名:
刘红梅: 7 次
张丹: 6 次
李娜: 5 次
王丹丹: 5 次
王海燕: 5 次
杨婷: 4 次
刘红: 4 次
李淑华: 4 次
杨成: 4 次
张秀华: 4 次

Step 3: 提取答案

# 获取重名最多的姓名和次数
top_name, top_count = most_common[0]

print(f'重名最多的人: {top_name}')
print(f'出现次数: {top_count}')
print(f'答案: {top_name}{top_count}')

输出:

重名最多的人: 刘红梅
出现次数: 7
答案: 刘红梅7

Step 4: 验证答案

为了确保答案正确,我们可以验证所有叫”刘红梅”的记录:

print('验证:所有叫"刘红梅"的记录')
print('=' * 80)

with open('#U5c45#U6c91#U4fe1#U606f#U8868.csv', 'r', encoding='utf-8-sig') as f:
    reader = csv.DictReader(f)
    count = 0
    
    for row in reader:
        try:
            name = base64.b64decode(row['姓名']).decode('utf-8')
            if name == '刘红梅':
                count += 1
                id_card = base64.b64decode(row['身份证号']).decode('utf-8')
                phone = base64.b64decode(row['手机号码']).decode('utf-8')
                address = base64.b64decode(row['家庭住址']).decode('utf-8')
                print(f'{count}. 序号:{row["序号"]:>4} | 身份证:{id_card} | 手机:{phone}')
        except:
            continue

print(f'确认:刘红梅 出现了 {count} 次')

输出:

验证:所有叫"刘红梅"的记录
================================================================================
1. 序号: 320 | 身份证:360429199301219709 | 手机:13242965575
2. 序号: 461 | 身份证:654224200208144330 | 手机:15145128603
3. 序号: 664 | 身份证:513422194903038431 | 手机:15835846265
4. 序号: 785 | 身份证:360402196106309326 | 手机:14573029873
5. 序号:1822 | 身份证:45042219530907884X | 手机:15654482963
6. 序号:1880 | 身份证:440701199811034851 | 手机:13170093945
7. 序号:1964 | 身份证:411282194701306231 | 手机:13714155998
================================================================================
确认:刘红梅 出现了 7 次

验证通过:确实有7个不同的人都叫”刘红梅”(身份证号和手机号都不同)

完整解题脚本

#!/usr/bin/env python3
import base64
import csv
from collections import Counter

def solve():
    # 读取CSV并解码所有姓名
    names = []
    
    with open('#U5c45#U6c11#U4fe1#U606f#U8868.csv', 'r', encoding='utf-8-sig') as f:
        reader = csv.DictReader(f)
        
        for row in reader:
            try:
                name_encoded = row['姓名']
                name = base64.b64decode(name_encoded).decode('utf-8')
                names.append(name)
            except:
                continue
    
    print(f'总共有 {len(names)} 条记录')
    print()
    
    # 统计姓名出现次数
    name_counter = Counter(names)
    
    # 找出出现次数最多的姓名
    most_common = name_counter.most_common(10)
    
    print('出现次数最多的前10个姓名:')
    print('=' * 60)
    for name, count in most_common:
        print(f'{name}: {count} 次')
    
    print()
    print('=' * 60)
    top_name, top_count = most_common[0]
    print(f'重名最多的人: {top_name}')
    print(f'出现次数: {top_count}')
    print()
    print(f'答案: {top_name}{top_count}')
    
    return f'{top_name}{top_count}'

if __name__ == '__main__':
    answer = solve()

运行脚本:

python3 solve.py

一行命令解题

如果想要快速获取答案:

python3 -c "
import base64, csv
from collections import Counter
names = []
with open('#U5c45#U6c11#U4fe1#U606f#U8868.csv', 'r', encoding='utf-8-sig') as f:
    for row in csv.DictReader(f):
        try: names.append(base64.b64decode(row['姓名']).decode('utf-8'))
        except: pass
top = Counter(names).most_common(1)[0]
print(f'答案: {top[0]}{top[1]}')
"

Flag

刘红梅7

答案验证

7个叫”刘红梅”的人分别是:

序号身份证号手机号住址
32036042919930121970913242965575福建省合肥县高明王街86号12号楼2678室
46165422420020814433015145128603安徽省秀梅县海陵刘街58号
66451342219490303843115835846265河北省东县南溪北镇街61号7号楼1536室
78536040219610630932614573029873青海省兴安盟市崇文杨街59号
182245042219530907884X15654482963北京市红霞市沈河荆门街32号
188044070119981103485113170093945浙江省广州县怀柔潮州路19号15号楼1820室
196441128219470130623113714155998北京市贵阳县高明王街86号

可以看到,这7个人的身份证号、手机号、住址都不同,是真正的7个不同的人。

数据应急(第一题)

题目名称:磁盘取证 - 合同文件恢复
题目类型:数字取证 (Digital Forensics)
难度:中等

题目描述

黑客在攻击时,为了对公司造成更大的破坏,直接删除了磁盘中的文件。但好在系统有自动的磁盘备份计划,保留了一个备份磁盘。请你通过技术手段,恢复出黑客删除的文件。

答案要求

请找出删除的文件中的一个合同文件,提交合同编号。
例:如果合同编号为 HT-2023-003085,则最终提交答案为:HT-2023-003085

附件disk.img (1GB 磁盘镜像文件)

解题思路

这是一道典型的数字取证题,需要:

  1. 识别磁盘镜像的文件系统类型
  2. 使用数据恢复工具恢复已删除的文件
  3. 在恢复的文件中找到合同文件
  4. 提取合同编号

解题步骤

Step 1: 磁盘镜像基础分析

首先查看磁盘镜像的基本信息:

file disk.img

输出

disk.img: DOS/MBR boot sector, code offset 0x3c+2, OEM-ID "mkfs.fat", 
sectors/cluster 32, reserved sectors 32, root entries 512, Media descriptor 0xf8, 
sectors/FAT 256, sectors/track 63, heads 64, sectors 2097144 (volumes > 32 MB), 
serial number 0xdd894a27, unlabeled, FAT (16 bit)

关键信息

  • 文件系统类型:FAT16
  • 卷标:unlabeled(无卷标)
  • 总扇区数:2097144
  • 序列号:0xdd894a27

Step 2: 查看磁盘镜像十六进制头部

xxd disk.img | head -20

输出分析

00000000: eb3c 906d 6b66 732e 6661 7400 0220 2000  .<.mkfs.fat..  .
00000010: 0200 0200 00f8 0001 3f00 4000 0000 0000  ........?.@.....
00000020: f8ff 1f00 8000 2927 4a89 dd4e 4f20 4e41  ......)'J..NO NA
00000030: 4d45 2020 2020 4641 5431 3620 2020 0e1f  ME    FAT16   ..

关键发现

  • 字节 0x00-0x02: EB 3C 90 - FAT引导跳转指令
  • 字节 0x03-0x0A: mkfs.fat - OEM标识
  • 字节 0x36-0x3D: FAT16 - 文件系统类型确认

Step 3: 检查文件系统UUID

sudo blkid disk.img

输出

disk.img: SEC_TYPE="msdos" UUID="DD89-4A27" BLOCK_SIZE="512" TYPE="vfat"

确认文件系统为 VFAT (FAT16),块大小为512字节。

Step 4: 尝试查看可读字符串

strings disk.img | head -100

输出中的关键线索

mkfs.fat
NO NAME    FAT16
IN-SE~1ZIP
_____~1PDF
WIN-SERVER-PC-20251202-122722.raw

发现

  • 存在PDF文件的8.3短文件名格式:_____~1.PDF
  • 存在一个RAW文件引用
  • 文件名被删除后显示为下划线

Step 5: 使用Foremost恢复删除的文件

Foremost是一个基于文件头和文件尾特征的数据恢复工具(文件雕刻技术)。

# 创建输出目录
mkdir recovered_files

# 运行foremost恢复
foremost -i disk.img -o recovered_files -v

Foremost工作原理

  1. 扫描磁盘镜像中的二进制数据
  2. 识别已知文件类型的文件头(如PDF的%PDF-
  3. 识别文件尾(如PDF的%%EOF
  4. 提取完整的文件内容

输出

Foremost version 1.5.7 by Jesse Kornblum, Kris Kendall, and Nick Mikus
Audit File

Foremost started at Sun Dec 28 01:32:21 2025
Invocation: foremost -i disk.img -o recovered_files -v
Output directory: /home/kali/Desktop/DemoDir/recovered_files
Configuration file: /etc/foremost.conf

Processing: disk.img
|------------------------------------------------------------------
File: disk.img
Start: Sun Dec 28 01:32:21 2025
Length: 1024 MB (1073741824 bytes)

Num      Name (bs=512)         Size      File Offset     Comment
*0:     00351776.pdf           8 KB       180109312

**********|
Finish: Sun Dec 28 01:32:55 2025

1 FILES EXTRACTED
pdf:= 1
------------------------------------------------------------------
Foremost finished at Sun Dec 28 01:32:55 2025

关键信息

  • 成功恢复 1个PDF文件
  • 文件大小:8 KB
  • 文件偏移:180109312 字节(约172 MB处)
  • 文件名:00351776.pdf(Foremost自动命名,基于扇区号)

计算文件位置

  • 扇区号:351776
  • 字节偏移:351776 × 512 = 180109312 字节
  • 位置:~172 MB

Step 6: 查看恢复的文件

ls -R recovered_files/

输出

recovered_files/:
audit.txt  pdf

recovered_files/pdf:
00351776.pdf

文件结构

  • audit.txt - Foremost的审计日志
  • pdf/ - 恢复的PDF文件目录
    • 00351776.pdf - 恢复的合同文件

Step 7: 提取合同编号

打开恢复的PDF文件:

# 方法1: 直接打开PDF查看
xdg-open recovered_files/pdf/00351776.pdf

# 方法2: 转换为文本后搜索
pdftotext recovered_files/pdf/00351776.pdf - | grep "HT-"

# 方法3: 使用strings搜索
strings recovered_files/pdf/00351776.pdf | grep "HT-"

在PDF中发现合同编号

HT-2025-001234

Step 8: 验证答案格式

合同编号格式:HT-YYYY-NNNNNN

  • HT: 合同类型标识
  • 2025: 年份
  • 001234: 六位合同序号

Flag: HT-2025-001234

数据溯源- 【题目1】证书合成

请根据题目提供的证书关键参数,合成私钥解密证书。请选手找到id为285的参数合成的证书(参考附件:params.csv),可以解密哪个流量包(参考附件:pcap.zip)。并将其流量包名称作为答案提交。【答案标准】若id为285的参数合成证书,可以解密""UT5NHVWo2Z.pcap"",则答案提交为UT5NHVWo2Z.pcap的32位小写MD5值如7cb41b100d1cfbcbd1de1d795dac3fcb

题目分析

题目要求:

  1. 根据params.csv中id=285的参数合成RSA私钥
  2. 使用该私钥解密pcap.zip中的流量包
  3. 找出能被成功解密的流量包文件名
  4. 提交该文件名的MD5值作为答案

解题步骤

1. 提取ID=285的RSA参数

从params.csv中找到:

id: 285
e: 65537
p: 177264302295959185550899884811457697789837321132319354039496340545988969470422347313577084568610012957139649359576035974322283705879187577664768699213211347033624840318251940972496063336844685896882713624561971974788692556498019960846465311267474369690812099681875735569564330504277517754796899917257323134723
q: 143990163909936129648804807321551478733567016733642335522156625973321506509458427490929508866189002322826874210961910641865602374675333206288577734876005828016379170951078934469472423840724164310343028554942665656763806178050620857070820746559094989518930589089970082522430002916286799917078785172333647028571

2. 计算RSA密钥参数

使用RSA算法计算:

  • n = p × q
  • φ(n) = (p-1) × (q-1)
  • d = e^(-1) mod φ(n)

计算结果:

n = 25524315942975630524902164348980646615415631379067906424905092637569433934236415697372635457461090451139209839638834636903322814809175304746826901103004216102634794552592149840624150935097695947885611303063109481472573238304773329443506200495854657482651549528147796059266681963958098875144795832902194879880482303139612778003682586782728535759095884982208105029514574602777436707784410218940597817232989310469845223163728817066720212125675003809485140708725052901029868743431319305493958381372040484786766296673968301385185624931088751230885265032642815808366813676365040639645753384044321228415112355245904063170833

d = 722845574105661996341279232062927508403061047035263047204233515957838616078593581249120518318625873587658474790876559593703818366507917140090494353528172255161056787610220640313081528534130699898922663205290617477350137026315810907535399974370298370200059141008015464333108957155595030475437402778071527489167629069652916280419064485102236863714459375252132658482271427695730385678764768730893774682537210856041852181896672956592276075190253085972611046793782307817242610172767299787547389163753900144645425776915705020473062273536757021628523871664234759138128442757113837813811691074624911520811865965944689268993

3. 生成RSA私钥

使用Python的pycryptodome库构造RSA密钥对象并导出PEM格式私钥。

4. 测试解密流量包

使用tshark工具配合生成的私钥对pcap.zip中的500个流量包进行TLS解密测试。

解密命令:

tshark -r <pcap_file> \
  -o "tls.keys_list:0.0.0.0,443,http,id285_key.pem" \
  -Y "http" \
  -T fields \
  -e http.request.uri

5. 结果

经过测试,发现只有 mAqY0WRHsV.pcap 能被成功解密。

解密后可以看到HTTP流量:

POST /api/v1/user/profile

这证明该证书与此流量包中的TLS会话匹配。

答案

文件名: mAqY0WRHsV.pcap

MD5值: 1f5c34eab5696a300afff7452b7f7e6a

验证

$ echo -n "mAqY0WRHsV.pcap" | md5sum
1f5c34eab5696a300afff7452b7f7e6a

🔔 想要获取更多网络安全与编程技术干货?

关注 泷羽Sec-静安 公众号,与你一起探索前沿技术,分享实用的学习资源与工具。我们专注于深入分析,拒绝浮躁,只做最实用的技术分享!💻

马上加入我们,共同成长!🌟

👉 长按或扫描二维码关注公众号

直接回复文章中的关键词,获取更多技术资料与书单推荐!📚

CTF